/**
 * User: alesj
 * Date: 2004.9.7
 * Time: 12:24:22
 */

package com.generalynx.ecos.web.form;

import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.validation.BindException;
import org.springframework.web.bind.RequestUtils;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.SimpleFormController;

import com.generalynx.common.constants.WebConst;
import com.generalynx.common.data.EntityComparator;
import com.generalynx.common.data.IEntity;
import com.generalynx.ecos.data.dao.IBasicDAO;
import com.generalynx.ecos.data.dao.IBasicDAOAware;
import com.generalynx.ecos.web.object.BeanCollection;
import com.generalynx.ecos.web.object.MemberSelector;

public abstract class AbstractSingleSelectForm extends SimpleFormController implements IBasicDAOAware {

    private IBasicDAO m_basicDAO;

    public IBasicDAO getBasicDAO() {
        return m_basicDAO;
    }

    public void setBasicDAO(IBasicDAO basicDAO) {
        m_basicDAO = basicDAO;
    }

    protected Object formBackingObject(HttpServletRequest request) throws Exception {
        return new MemberSelector();
    }

    protected abstract String getHeaderLabel();
    protected abstract String getBodyLabel();
    protected abstract Class getEntityClass();
    protected abstract Class getMembersClass();
    protected abstract Set getMembers(IEntity entity);

    protected ModelAndView showForm(HttpServletRequest request,
                                    HttpServletResponse response,
                                    BindException errors) throws Exception {

        IEntity entity = getBasicDAO().loadEntity(getEntityClass(), RequestUtils.getRequiredIntParameter(request, "id"));
        List all = getAll();
        Set members = getMutableMembers(entity);
        Set notMembers = new TreeSet(new EntityComparator());
        Iterator iter = all.iterator();
        while (iter.hasNext()) {
            IEntity e = (IEntity)iter.next();
            if (!members.contains(e)) {
                notMembers.add(e);
            }
        }

        request.setAttribute(WebConst.HEADER_LABEL, getHeaderLabel());
        request.setAttribute(WebConst.BODY_LABEL, getBodyLabel());
        request.setAttribute(WebConst.ENTITY, entity);
        request.setAttribute(WebConst.LEFT, new BeanCollection(notMembers));
        request.setAttribute(WebConst.RIGHT, new BeanCollection(members));

        return super.showForm(request, response, errors);
    }

    protected ModelAndView onSubmit(HttpServletRequest request,
                                    HttpServletResponse response,
                                    Object command,
                                    BindException errors) throws Exception {

        MemberSelector ms = (MemberSelector)command;
        int id = ms.getId();
        String action = ms.getAction();
        IEntity entity = getBasicDAO().loadEntity(getEntityClass(), id);
        Set members = getMembers(entity);

        if ("left".equalsIgnoreCase(action) && ms.getLeft() != null) {
            doLeft(ms.getLeft(), members, entity);
        } else if ("right".equalsIgnoreCase(action) && ms.getRight() != null) {
            doRight(ms.getRight(), members, entity);
        } else {
            return new ModelAndView(getSuccessView(), WebConst.MSG_CODE, "saved");
        }
        getBasicDAO().saveOrUpdateEntity(entity);
        return showForm(request, response, errors);
    }

    /* Convinience methods for possible overridding. */

    protected List getAll() {
        return getBasicDAO().findEntity(getMembersClass());
    }

    /**
     * If needed, selectable members can be duplicated - thus override this function.
     */
    protected Set getMutableMembers(IEntity entity) {
        return getWrappedEntitySet(getMembers(entity));
    }

    /**
     * Compares objects by entity id. 
     */
    protected Set getWrappedEntitySet(Set set) {
        Set entitySet = new TreeSet(new EntityComparator());
        entitySet.addAll(set);
        return entitySet;
    }

    /**
     * Adding new members.
     * Must be overridden when join table has some extra properties.
     */
    protected void doLeft(int[] left, Set members, IEntity entity) {
        for (int i = 0; i < left.length; i++) {
            IEntity member = getBasicDAO().loadEntity(getMembersClass(), left[i]);
            boolean exists = !members.add(member);
            postAddition(entity, member, exists);
            if (exists) {
                logger.warn(entity + " [" + entity.getId() + "] already contained member " +
                            member + " [" + member.getId() + "].");
            }
        }
    }

    protected void postAddition(IEntity entity, IEntity member, boolean exists) {
    }

    /**
     * Removing members.
     */
    protected void doRight(int[] right, Set members, IEntity entity) {
        for (int i = 0; i < right.length; i++) {
            IEntity member = getBasicDAO().loadEntity(getMembersClass(), right[i]);
            boolean exists = members.remove(member);
            postRemoval(entity, member, exists);
            if (!exists) {
                logger.warn(entity + " [" + entity.getId() + "] didn't contain member " +
                            member + " [" + member.getId() + "].");
            }
        }
    }

    protected void postRemoval(IEntity entity, IEntity member, boolean exists) {
    }

}
